package com.forte.fortePoiUtils.merger;

import com.forte.fortePoiUtils.commons.Datas;
import com.forte.fortePoiUtils.exception.ExcelTypeException;
import com.forte.fortePoiUtils.typeEnum.ExcelType;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 *
 * excel合并器
 *
 * 合并器会将多个excel文件合并为一个excel文件，并根据文件名或者sheet名对新的sheet表进行命名
 *
 * @author ForteScarlet <[email]ForteScarlet@163.com>
 * @since JDK1.8
 **/
public abstract class ExcelMerger implements Closeable {

    private final OutputStream out;

    private static final String MSG_STREAM_LINKED = "stream has already been operated upon or closed";

    /** sheet获取函数列表 */
    private List<Supplier<Sheet>> sheetSupplierList = new ArrayList<>();

    /** 输出至指定输出流 */
    public abstract void merger() throws IOException;

    protected OutputStream getOut(){
        return out;
    }
    /**
     * 获取sheet表流
     */
    protected Stream<Sheet> getSheets(){
        if(sheetSupplierList == null){
            throw new IllegalStateException(MSG_STREAM_LINKED);
        }
        Stream<Sheet> stream = sheetSupplierList.stream().map(Supplier::get).peek(s -> System.gc());
        sheetSupplierList = null;
        return stream;
    }

    /**
     * 追加一个表
     * @param path 文件
     * @param sheetName sheet名称
     */
    public ExcelMerger append(File path, String sheetName) throws FileNotFoundException {
        String fileName = path.getName();
        //如果不是一个文件，则直接抛出异常
        if(!path.isFile())
            throw new ExcelTypeException("这不是一个文件！");
        //大于1，有扩展名
        if(fileName.split("\\.").length > 1){
            if(fileName.endsWith("." + Datas.HSSF_END))
                return append(new FileInputStream(path), ExcelType.HSSF, sheetName);
            if(fileName.endsWith("." + Datas.XSSF_END))
                return append(new FileInputStream(path), ExcelType.XSSF, sheetName);
                //如果扩展名错误，抛出错误
            else
                throw new ExcelTypeException();
        }else{
            //没有扩展名 , 视为XSSF
            return append(new FileInputStream(path), ExcelType.XSSF, sheetName);
        }
    }

    /**
     * 追加一个表
     * @param in 输入流
     * @param type  表类型
     * @param sheetName sheet名称
     */
    public ExcelMerger append(InputStream in, ExcelType type, String sheetName){
        if(sheetSupplierList == null){
            throw new IllegalStateException(MSG_STREAM_LINKED);
        }
        //获取工作簿
        sheetSupplierList.add(() -> {
            // 获取工作簿
            Workbook book = type.getWorkbook(in);
            Sheet sheet = book.getSheet(sheetName);
            if(sheet == null){
                throw new NullPointerException("无法获取sheet表：(name: " + sheetName + ')');
            }
            return sheet;
        });
        return this;
    }

    /**
     * 追加一个表
     * @param path 文件
     * @param sheetIndex sheet名称
     */
    public ExcelMerger append(File path, int sheetIndex) throws FileNotFoundException {
        String fileName = path.getName();
        //如果不是一个文件，则直接抛出异常
        if(!path.isFile())
            throw new ExcelTypeException("这不是一个文件！");
        //大于1，有扩展名
        if(fileName.split("\\.").length > 1){
            if(fileName.endsWith("." + Datas.HSSF_END))
                return append(new FileInputStream(path), ExcelType.HSSF, sheetIndex);
            if(fileName.endsWith("." + Datas.XSSF_END))
                return append(new FileInputStream(path), ExcelType.XSSF, sheetIndex);
                //如果扩展名错误，抛出错误
            else
                throw new ExcelTypeException();
        }else{
            //没有扩展名 , 视为XSSF
            return append(new FileInputStream(path), ExcelType.XSSF, sheetIndex);
        }
    }


    /**
     * 追加一个表
     * @param in 输入流
     * @param type  表类型
     * @param sheetIndex sheet索引
     */
    public ExcelMerger append(InputStream in, ExcelType type, int sheetIndex){
        if(sheetSupplierList == null){
            throw new IllegalStateException(MSG_STREAM_LINKED);
        }
        //获取工作簿
        sheetSupplierList.add(() -> {
            try {
                // 获取工作簿
                Workbook book = type.getWorkbook(in);
                Sheet sheet = book.getSheetAt(sheetIndex);
                if(sheet == null){
                    throw new NullPointerException("无法获取sheet表：(index: " + sheetIndex + ')');
                }
                return sheet;
            }catch (Exception e){
                throw new RuntimeException(e);
            }finally {
                try {
                    in.close();
                } catch (IOException ignored) {
                }
            }
        });
        return this;
    }

    /**
     * 追加一个表
     * 默认为索引为0的sheet
     * @param in    输入流
     * @param type  类型
     * @return
     */
    public ExcelMerger append(InputStream in, ExcelType type){
        return append(in, type, 0);
    }

    /**
     * 追加一个表
     * 默认为索引为0的sheet
     * @param file  文件对象
     * @return
     */
    public ExcelMerger append(File file) throws FileNotFoundException {
        return append(file, 0);
    }


    /** 追加所有的表 */
    public ExcelMerger appendAllSheet(InputStream in, ExcelType type){
        if(sheetSupplierList == null){
            throw new IllegalStateException(MSG_STREAM_LINKED);
        }
        Workbook book = type.getWorkbook(in);
        for (int i = 0; i < book.getNumberOfSheets(); i++) {
            final int index = i;
            sheetSupplierList.add(() -> book.getSheetAt(index));
        }
        return this;
    }

    /** 追加所有的表 */
    public ExcelMerger appendAllSheet(File path) throws FileNotFoundException {
        if(sheetSupplierList == null){
            throw new IllegalStateException(MSG_STREAM_LINKED);
        }

        ExcelType type;

        String fileName = path.getName();
        //如果不是一个文件，则直接抛出异常
        if(!path.isFile())
            throw new ExcelTypeException("这不是一个文件！");
        //大于1，有扩展名
        if(fileName.split("\\.").length > 1){
            if(fileName.endsWith("." + Datas.HSSF_END))
                type = ExcelType.HSSF;
            if(fileName.endsWith("." + Datas.XSSF_END))
                type = ExcelType.XSSF;
                //如果扩展名错误，抛出错误
            else
                throw new ExcelTypeException();
        }else{
            //没有扩展名 , 视为XSSF
            type = ExcelType.XSSF;
        }

        Workbook book = type.getWorkbook(new FileInputStream(path));
        for (int i = 0; i < book.getNumberOfSheets(); i++) {
            final int index = i;
            sheetSupplierList.add(() -> book.getSheetAt(index));
        }
        return this;
    }


    public ExcelMerger(OutputStream out){
        this.out = out;
    }

    public ExcelMerger(File out) throws FileNotFoundException {
        this.out = new FileOutputStream(out);
    }

    @Override
    public void close() throws IOException {
        out.close();
    }

}
